//
//	MiscMergeDriver.m -- a simple loop for driving bulk merges
//		Written by Don Yacktman Copyright (c) 1995 by Don Yacktman.
//				Version 1.0.  All rights reserved.
//		This notice may not be removed from this source code.
//
//	This object is included in the MiscKit by permission from the author
//	and its use is governed by the MiscKit license, found in the file
//	"LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
//	for a list of all applicable permissions and restrictions.
//	

#import <misckit/misckit.h>
#import <misckit/miscmerge.h>

@implementation MiscMergeDriver
/*" A MiscMergeDriver is used to merge an ASCII template with
several dictionaries filled with key/value pairs.  Each dictionary
will be used in turn to generate a new output ªdocumentº.

If you only need to generate a single merge, you may wish to
simply use a MiscMergeEngine object.  If you have several
merges to perform, then a MiscMergeDriver implements the
required loop to generate the required merges, as well as
supporting a protocol that allows the merge engine some
control over the loop.  If you create your own loop, instead
of using a MiscMergeDriver instance, some
of the merge commands such as ªnextº will be ignored rather
than performing the desired function.

To use a MiscMergeDriver you must provide it with a template,
dictionaries to merge into the template, and, optionally, a
MiscMergeEngine instance.  If a MiscMergeEngine is  not provided,
one will be created to perform the merge.  To set up a merge
template, use the -#{setTemplate:} method.  It expects an instance
of the MiscMergeTemplate class, which comes from an ASCII file or
from a MiscString object.

The data to be merged into the template
is set up using the -#{setMergeData:} method.  The data should be
stored as key/value pairs in a MiscDictionary object for each
merge to be performed.  Place all the dictionaries into a List
object and use the List object as the argument to -#{setMergeData:}.

Finally, use the -#{doMerge:} method to perform the desired
merge operation.  The results will be returned as a List
object with a MiscString corresponding to each MiscDictionary
in the List provided to the MiscMergeDriver by the most recent -#{setData:}
message.  For example, the third MiscString will contain the
results from the merge with the third MiscDictionary.  If
the Merge returned no result (due to an error or an ªomitº
command, for example) then the MiscString will be empty.

If you wish to use a specific subclass of MiscMergeEngine to
perform the merge, then use the -#{setEngine:} method to set
up the engine before calling -#{doMerge:}.  This engine will
be used for all subsequent merges unless -#{setEngine:} is sent again.

For more information, please see the IntroMiscMerge.rtfd document.
It describes the syntax of the merge language and built-in
commands available.  The MiscMergeArchitecture.rtfd document
describes the architecutre of the various classes used to
perform merging operations and how to add custom commands to
the framework.
"*/

- (MiscMergeEngine *)engine
/*" Returns the merge engine, an instance of MiscMergeEngine,
that will be used to perform a merge.  If no engine has been
set up, then nil is returned.
"*/
{
	return engine;
}

- (List *)mergeData
/*" Returns the List of MiscDictionaries that will be used for the next merge.
"*/
{
	return dictionaries;
}

- (MiscMergeTemplate *)template
/*" Returns the MiscMergeTemplate that will be used for the next merge.
"*/
{
	return template;
}

- setTemplate:aTemplate
/*" Sets the MiscMergeTemplate that will be used for the next merge.
Returns self upon success and nil upon failure.  This method fails if a
merge is in progress.
"*/
{ // we don't free the old -- watch for memory leaks!
	if (merging) return nil;
	template = aTemplate;
	return self;
}

- setMergeData:(List *)aList
/*" Sets the List of MiscDictionaries that will be used for the next merge.
Returns self upon success and nil upon failure.  This method fails if a
merge is in progress.
"*/
{ // we don't free the old -- watch for memory leaks!
	if (merging) return nil;
	dictionaries = aList;
	return self;
}

- (List *)doMerge:sender
/*" Sets up a merge engine, if necessary, and performs a merge of
the template with the MiscDictionaries in the data List.  Any
engines created will be destroyed after the merge; engines set
using -#{setEngine} will persist, however.  A List object populated
with MiscStrings will be returned.  There is a one-to-one correspondence
between the index of the return MiscStrings in the List and the
MiscDictionaries' indices in the List that was provided via the
most recent -#{setMergeData}.  Thus, if there were six dictionaries
used for merging, six MiscStrings will be returned, as the result
of six merges.  Note that the ªnextº command will cause a MiscMergeEngine
to attempt to skip forward to the next MiscDictionary, while still
performing a single merge.  In this case, an empty MiscString will
be inserted in the output List as a placeholder and the final
merge result will be put in the slot corresponding to the last
dictionary used.  Merges that fail or are halted due to an ªomitº
command will also be represented by an empty MiscString in the output.
"*/
{ // YOU have to free the returned list.
	BOOL createdEngine = NO;

	if (merging) return nil; // not re-entrant!!!
	if (!template || !dictionaries) return nil;
	if ([dictionaries count] < 1) return nil;
	if (!engine) {
		createdEngine = YES;
		engine = [MiscMergeEngine newWithTemplate:template];
	}

	merging = YES;
	output = [[List alloc] init];
	for (_mergeLoopIndex=0; _mergeLoopIndex<[dictionaries count];
			_mergeLoopIndex++) {
		id ret = [engine mergeWithDictionary:
				[dictionaries objectAt:_mergeLoopIndex]
				sender:self];
		while (_mergeLoopIndex > [output count]) {
			[output addObject:[MiscString new]]; // placeholder for skipped
		}
		if (ret) [output addObject:ret];
		else [output addObject:[MiscString new]]; // placeholder if failed
	}
	if (createdEngine) { // don't keep it hanging around
		[engine free];
		engine = nil;
	}
	merging = NO;
	return output;
}

- setEngine:(MiscMergeEngine *)anEngine
/*" Sets up an engine to be used for merging.  If no engine is set, a
temporary engine will be created before and used during a merge.  It
will be destroyed after it is used.  Engines set using -#{setEngine:}
will not be destroyed at the end of a merge and will be used for
subsequent merges as well.  Setting a new engine will not free the
old engine; the MiscMergeDriver does not ªownº the engine; it only
makes use of it.  This way, the same engine could be used by several
MiscMergeDriver instances.  Setting the engine to nil will revert to
the default create/use/destroy pattern.  The engine cannot be changed
while a merge loop is in progress.  Returns self if successful or
nil if failure occurs.
"*/
{
	if (!merging) {
		engine = anEngine;
		return self;
	} else {
		return nil;
	}
}

// Methods required by the MiscMergeDriver protocol

// Advance merge loop and return the next dictionary, nil if no more left
- advanceMergeLoop
/*" During a merge, returns the next dictionary that will be merged and advances the merge loop.  Returns nil if not merging or if the loop is
already performing the last merge.
"*/
{
	if ((!merging) || (_mergeLoopIndex >= [dictionaries count])) return nil;
	_mergeLoopIndex++;
	if (_mergeLoopIndex >= [dictionaries count]) return nil;
	return [dictionaries objectAt:_mergeLoopIndex];
}

- currentDictionary
/*" During a merge, returns the dictionary that is currently being merged.
Returns nil otherwise.
"*/
{
	if (!merging) return nil;
	if (_mergeLoopIndex >= [dictionaries count]) return nil;
	return [dictionaries objectAt:_mergeLoopIndex];
}

@end
